27.2 工具调用机制

17 分钟阅读

工具调用机制概述#

工具调用(Tool Calling)是Agentic AI系统的核心能力之一,它允许AI模型通过调用外部工具来扩展其能力,执行实际的操作,而不仅仅是生成文本。

工具调用的基本概念#

1. 什么是工具调用#

工具调用是指AI模型根据用户的请求,识别需要使用的工具,生成工具调用参数,执行工具调用,并将结果整合到最终响应中的过程。

工具调用的特点:

  • 结构化输出: 模型输出结构化的工具调用指令
  • 参数验证: 验证工具调用参数的有效性
  • 结果整合: 将工具执行结果整合到响应中
  • 错误处理: 处理工具调用失败的情况

2. 工具调用流程#

用户请求 ↓ 意图识别 ↓ 工具选择 ↓ 参数生成 ↓ 工具执行 ↓ 结果处理 ↓ 响应生成

工具定义与注册#

1. 工具定义#

python
python # 示例:工具定义 用户请求: "定义一个文件读取工具" Claude Code 生成的代码: ```python ```python from typing import Dict, Any, Optional import json from dataclasses import dataclass, asdict from enum import Enum class ToolType(Enum): """工具类型""" FILE_OPERATION = "file_operation" CODE_EXECUTION = "code_execution" API_CALL = "api_call" SEARCH = "search" CALCULATION = "calculation" @dataclass class ToolParameter: """工具参数""" name: str type: str description: str required: bool default: Optional[Any] = None @dataclass class ToolDefinition: """工具定义""" name: str description: str type: ToolType parameters: list[ToolParameter] function: callable def to_dict(self) -> Dict[str, Any]: """转换为字典""" return { 'name': self.name, 'description': self.description, 'type': self.type.value, 'parameters': [asdict(param) for param in self.parameters] } # 文件读取工具 def read_file(file_path: str) -> str: """读取文件内容""" try: with open(file_path, 'r', encoding='utf-8') as f: return f.read() except FileNotFoundError: return f"Error: File not found: {file_path}" except Exception as e: return f"Error: {str(e)}" read_file_tool = ToolDefinition( name="read_file", description="读取指定文件的内容", type=ToolType.FILE_OPERATION, parameters=[ ToolParameter( name="file_path", type="string", description="要读取的文件路径", required=True ) ], function=read_file ) # 文件写入工具 def write_file(file_path: str, content: str) -> str: """写入内容到文件""" try: with open(file_path, 'w', encoding='utf-8') as f: f.write(content) return f"Success: File written to {file_path}" except Exception as e: return f"Error: {str(e)}" write_file_tool = ToolDefinition( name="write_file", description="将内容写入到指定文件", type=ToolType.FILE_OPERATION, parameters=[ ToolParameter( name="file_path", type="string", description="要写入的文件路径", required=True ), ToolParameter( name="content", type="string", description="要写入的内容", required=True ) ], function=write_file ) # 代码执行工具 def execute_code(code: str, language: str = "python") -> str: """执行代码""" try: if language == "python": import subprocess result = subprocess.run( ['python', '-c', code], capture_output=True, text=True, timeout=10 ) if result.returncode == 0: return result.stdout else: return f"Error: {result.stderr}" else: return f"Error: Unsupported language: {language}" except subprocess.TimeoutExpired: return "Error: Code execution timeout" except Exception as e: return f"Error: {str(e)}" execute_code_tool = ToolDefinition( name="execute_code", description="执行代码并返回结果", type=ToolType.CODE_EXECUTION, parameters=[ ToolParameter( name="code", type="string", description="要执行的代码", required=True ), ToolParameter( name="language", type="string", description="编程语言", required=False, default="python" ) ], function=execute_code ) ### 2. 工具注册 ```python # 示例:工具注册 用户请求: "实现一个工具注册系统" Claude Code 生成的代码: ```python ```python from typing import Dict, List, Optional class ToolRegistry: """工具注册表""" def __init__(self): self.tools: Dict[str, ToolDefinition] = {} self.categories: Dict[str, List[str]] = {} def register(self, tool: ToolDefinition, category: Optional[str] = None): """注册工具""" self.tools[tool.name] = tool if category: if category not in self.categories: self.categories[category] = [] self.categories[category].append(tool.name) def unregister(self, tool_name: str): """注销工具""" if tool_name in self.tools: del self.tools[tool_name] for category, tools in self.categories.items(): if tool_name in tools: tools.remove(tool_name) def get_tool(self, tool_name: str) -> Optional[ToolDefinition]: """获取工具""" return self.tools.get(tool_name) def list_tools(self, category: Optional[str] = None) -> List[ToolDefinition]: """列出工具""" if category: tool_names = self.categories.get(category, []) return [self.tools[name] for name in tool_names] else: return list(self.tools.values()) def search_tools(self, keyword: str) -> List[ToolDefinition]: """搜索工具""" results = [] keyword = keyword.lower() for tool in self.tools.values(): if (keyword in tool.name.lower() or keyword in tool.description.lower()): results.append(tool) return results def get_tool_schema(self, tool_name: str) -> Optional[Dict[str, Any]]: """获取工具schema""" tool = self.get_tool(tool_name) if tool: return tool.to_dict() return None def get_all_schemas(self) -> Dict[str, Dict[str, Any]]: """获取所有工具schema""" return { name: tool.to_dict() for name, tool in self.tools.items() } # 使用示例 registry = ToolRegistry() # 注册工具 registry.register(read_file_tool, category="file_operations")

registry.register(write_file_tool, category="file_operations") registry.register(execute_code_tool, category="code_execution")

列出所有工具

print("All tools:") for tool in registry.list_tools(): print(f" - {tool.name}: {tool.description}")

列出特定类别的工具

print("\nFile operation tools:") for tool in registry.list_tools(category="file_operations"): print(f" - {tool.name}: {tool.description}")

搜索工具

print("\nSearch results for 'file':") for tool in registry.search_tools("file"): print(f" - {tool.name}: {tool.description}")

获取工具schema

print("\nTool schema for read_file:") print(json.dumps(registry.get_tool_schema("read_file"), indent=2))

工具调用执行#

1. 工具调用解析#

示例:工具调用解析

用户请求: "实现工具调用解析器" Claude Code 生成的代码:

python
`python import re from typing import Dict, Any, List, Optional import json class ToolCallParser: """工具调用解析器""" def __init__(self, registry: ToolRegistry): self.registry = registry def parse_tool_calls(self, text: str) -> List[Dict[str, Any]]: """解析工具调用""" tool_calls = [] # 匹配工具调用模式 pattern = r'<tool_call>\s*<name>(.*?)</name>\s*<parameters>(.*?)</parameters>\s*</tool_call>' matches = re.finditer(pattern, text, re.DOTALL) for match in matches: tool_name = match.group(1).strip() parameters_str = match.group(2).strip() # 解析参数 try: parameters = json.loads(parameters_str) except json.JSONDecodeError: parameters = self._parse_parameters(parameters_str) tool_calls.append({ 'tool': tool_name, 'parameters': parameters }) return tool_calls def _parse_parameters(self, parameters_str: str) -> Dict[str, Any]: """解析参数字符串""" parameters = {} # 匹配参数 param_pattern = r'<(\w+)>(.*?)</\1>' matches = re.findall(param_pattern, parameters_str, re.DOTALL) for name, value in matches: parameters[name] = value.strip() return parameters def validate_tool_call(self, tool_call: Dict[str, Any]) -> tuple[bool, Optional[str]]: """验证工具调用""" tool_name = tool_call['tool'] parameters = tool_call['parameters'] # 检查工具是否存在 tool = self.registry.get_tool(tool_name) if not tool: return False, f"Tool not found: {tool_name}" # 检查必需参数 for param in tool.parameters: if param.required and param.name not in parameters: return False, f"Missing required parameter: {param.name}" # 检查参数类型 for param in tool.parameters: if param.name in parameters: value = parameters[param.name] if not self._check_parameter_type(value, param.type): return False, f"Invalid type for parameter {param.name}: expected {param.type}" return True, None def _check_parameter_type(self, value: Any, expected_type: str) -> bool: """检查参数类型""" type_mapping = { 'string': str, 'integer': int, 'float': float, 'boolean': bool, 'array': list, 'object': dict } expected_python_type = type_mapping.get(expected_type) if expected_python_type: return isinstance(value, expected_python_type) return True # 使用示例 parser = ToolCallParser(registry) # 解析工具调用 text = """ I'll read the file for you. <tool_call> <name>read_file</name> <parameters>{"file_path": "/path/to/file.txt"}</parameters> </tool_call> """ tool_calls = parser.parse_tool_calls(text) print("Parsed tool calls:") for tool_call in tool_calls: print(f" Tool: {tool_call['tool']}") print(f" Parameters: {tool_call['parameters']}") # 验证工具调用 is_valid, error = parser.validate_tool_call(tool_call) if is_valid: print(f" Status: Valid") else: print(f" Status: Invalid - {error}") ```### 2. 工具调用执行 ``` python # 示例:工具调用执行 用户请求: "实现工具调用执行器" Claude Code 生成的代码: from typing import Dict, Any, List import asyncio from concurrent.futures import ThreadPoolExecutor class ToolExecutor: """工具执行器""" def __init__(self, registry: ToolRegistry): self.registry = registry self.executor = ThreadPoolExecutor(max_workers=4) async def execute_tool_call(self, tool_call: Dict[str, Any]) -> Dict[str, Any]: """执行工具调用""" tool_name = tool_call['tool'] parameters = tool_call['parameters'] # 获取工具 tool = self.registry.get_tool(tool_name) if not tool: return { 'success': False, 'error': f"Tool not found: {tool_name}", 'tool': tool_name } try: # 执行工具 loop = asyncio.get_event_loop() result = await loop.run_in_executor( self.executor, tool.function, **parameters ) return { 'success': True, 'result': result, 'tool': tool_name } except Exception as e: return { 'success': False, 'error': str(e), 'tool': tool_name } async def execute_tool_calls(self, tool_calls: List[Dict[str, Any]]) -> List[Dict[str, Any]]: """执行多个工具调用""" tasks = [ self.execute_tool_call(tool_call) for tool_call in tool_calls ] results = await asyncio.gather(*tasks, return_exceptions=True) return results def execute_tool_call_sync(self, tool_call: Dict[str, Any]) -> Dict[str, Any]: """同步执行工具调用""" tool_name = tool_call['tool'] parameters = tool_call['parameters'] # 获取工具 tool = self.registry.get_tool(tool_name) if not tool: return { 'success': False, 'error': f"Tool not found: {tool_name}", 'tool': tool_name } try: # 执行工具 result = tool.function(**parameters) return { 'success': True, 'result': result, 'tool': tool_name } except Exception as e: return { 'success': False, 'error': str(e), 'tool': tool_name } # 使用示例 executor = ToolExecutor(registry) # 执行工具调用 tool_calls = [ { 'tool': 'read_file', 'parameters': {'file_path': '/path/to/file.txt'} }, { 'tool': 'execute_code', 'parameters': {'code': 'print("Hello, World!")', 'language': 'python'} } ] # 异步执行 async def main(): results = await executor.execute_tool_calls(tool_calls) print("Execution results:") for result in results: if result['success']: print(f" {result['tool']}: Success") print(f" Result: {result['result']}") else: print(f" {result['tool']}: Failed") print(f" Error: {result['error']}") # 同步执行 def main_sync(): for tool_call in tool_calls: result = executor.execute_tool_call_sync(tool_call) if result['success']: print(f"{result['tool']}: Success") print(f" Result: {result['result']}") else: print(f"{result['tool']}: Failed") print(f" Error: {result['error']}") `> > ## 工具调用优化 ### 1. 缓存机制 ```python python # 示例:工具调用缓存 用户请求: "实现工具调用缓存" Claude Code 生成的代码: ```python ```python from typing import Dict, Any, Optional import hashlib import json from datetime import datetime, timedelta class ToolCallCache: """工具调用缓存""" def __init__(self, ttl: int = 3600): self.cache: Dict[str, Dict[str, Any]] = {} self.ttl = ttl def _generate_key(self, tool_name: str, parameters: Dict[str, Any]) -> str: """生成缓存键""" key_data = { 'tool': tool_name, 'parameters': parameters } key_str = json.dumps(key_data, sort_keys=True) return hashlib.md5(key_str.encode()).hexdigest() def get(self, tool_name: str, parameters: Dict[str, Any]) -> Optional[Dict[str, Any]]: """获取缓存""" key = self._generate_key(tool_name, parameters) if key in self.cache: cached = self.cache[key] # 检查是否过期 if datetime.utcnow() - cached['timestamp'] < timedelta(seconds=self.ttl): return cached['result'] else: del self.cache[key] return None def set(self, tool_name: str, parameters: Dict[str, Any], result: Dict[str, Any]): """设置缓存""" key = self._generate_key(tool_name, parameters) self.cache[key] = { 'result': result, 'timestamp': datetime.utcnow() } def clear(self): """清空缓存""" self.cache.clear() def cleanup(self): """清理过期缓存""" current_time = datetime.utcnow() expired_keys = [ key for key, cached in self.cache.items() if current_time - cached['timestamp'] >= timedelta(seconds=self.ttl) ] for key in expired_keys: del self.cache[key] class CachedToolExecutor(ToolExecutor): """带缓存的工具执行器""" def __init__(self, registry: ToolRegistry, cache: ToolCallCache): super().__init__(registry) self.cache = cache async def execute_tool_call(self, tool_call: Dict[str, Any]) -> Dict[str, Any]: """执行工具调用(带缓存)""" tool_name = tool_call['tool'] parameters = tool_call['parameters'] # 检查缓存 cached_result = self.cache.get(tool_name, parameters) if cached_result: return cached_result # 执行工具调用 result = await super().execute_tool_call(tool_call) # 缓存结果 if result['success']: self.cache.set(tool_name, parameters, result) return result ### 2. 批量执行 ```python # 示例:批量工具调用 用户请求: "实现批量工具调用" Claude Code 生成的代码: ```python ```python from typing import Dict, Any, List import asyncio class BatchToolExecutor(ToolExecutor): """批量工具执行器""" async def execute_batch(self, batch: List[Dict[str, Any]]) -> List[Dict[str, Any]]: """批量执行工具调用""" # 按工具类型分组 tool_groups = {} for tool_call in batch: tool_name = tool_call['tool'] if tool_name not in tool_groups: tool_groups[tool_name] = [] tool_groups[tool_name].append(tool_call) # 并行执行不同工具的调用 tasks = [ self._execute_tool_group(tool_name, tool_calls) for tool_name, tool_calls in tool_groups.items() ] results = await asyncio.gather(*tasks, return_exceptions=True) # 合并结果 all_results = [] for result_list in results: if isinstance(result_list, list): all_results.extend(result_list) return all_results async def _execute_tool_group(self, tool_name: str, tool_calls: List[Dict[str, Any]]) -> List[Dict[str, Any]]: """执行同一工具的多个调用""" tool = self.registry.get_tool(tool_name) if not tool: return [ { 'success': False, 'error': f"Tool not found: {tool_name}", 'tool': tool_name } for _ in tool_calls ] results = [] for tool_call in tool_calls: result = await super().execute_tool_call(tool_call) results.append(result) return results ``` ## 总结 工具调用机制包括: 1. **工具调用的基本概念**: 什么是工具调用、工具调用流程 2. **工具定义与注册**: 工具定义、工具注册 3. **工具调用执行**: 工具调用解析、工具调用执行 4. **工具调用优化**: 缓存机制、批量执行 通过工具调用机制,Claude Code可以执行各种实际操作,大大扩展了其能力。 在下一节中,我们将探讨自主规划算法。 ``` ```

标记本节教程为已读

记录您的学习进度,方便后续查看。